package poolserver

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"strconv"
	"strings"
	"sync/atomic"
)

func Load(configFile string) error {

	confFp, err := os.Open(configFile)
	if err != nil {
		return err
	}
	defer func() { confFp.Close() }()

	buff := bufio.NewReader(confFp)

	var key string
	var val string
	var lineno = 0
	var pool *Pool
	var idx int
	var ok bool
	var poolMapById = make(map[string]*(Pool))

	CleanAllPools()

	for {
		line, err := buff.ReadString('\n')
		lineno++

		if err != nil || io.EOF == err {
			break
		}

		line = strings.TrimSpace(line)
		if strings.HasPrefix(line, "#") {
			continue
		}

		if len(line) < 3 {
			continue
		}

		idx = strings.Index(line, "=")
		if len(line) <= 1 {
			continue
		}
		if idx < 0 {
			return fmt.Errorf("invalid line(no '='): %s ", line)
		}

		key = line[0:idx]
		key = strings.TrimSpace(key)

		val = line[idx+1:]

		// 把一行后的 以#的注释去掉
		sharpPos := strings.Index(val, "#")
		if sharpPos >= 0 {
			val = val[:sharpPos]
		}
		val = strings.TrimSpace(val)

		if !strings.HasPrefix(key, "pool.") { /*跳过不是以pool.开头的配置*/
			continue
		}

		items := strings.Split(key, ".")
		if len(items) != 3 {
			return fmt.Errorf("invalid item(%s) in %d line: %s", key, lineno, line)
		}

		pool, ok = poolMapById[items[1]]
		if !ok { /*如果没有匹配到则添加到map中*/
			pool = new(Pool)
			pool.Init()
			/*根据配置文件中的 pool.x.y 格式的 key 中的 x 作为 PoolConfig 的 ID 进行赋值*/
			pool.ID = items[1]
			pool.MsgBufSize = 65536
			poolMapById[items[1]] = pool
		}

		switch strings.ToLower(items[2]) {
		case "fe_max_conns":
			iv, _ := strconv.Atoi(val)
			atomic.StoreInt32(&pool.FeMaxConns, int32(iv))
		case "fe_user":
			pool.FeUser = val
		case "fe_passwd":
			pool.FePasswd = val
		case "fe_dbname":
			pool.FeDbName = val
		case "be_ipport":
			portalList := strings.Split(val, ",")
			for _, portal := range portalList {
				bePool := new(BePool)
				bePool.Init()
				pool.BePools[portal] = bePool
			}

		case "be_user":
			pool.BeUser = val
		case "be_dbname":
			pool.BeDbName = val
		case "be_passwd":
			pool.BePasswd = val
		case "be_conns":
			iv, _ := strconv.Atoi(val)
			atomic.StoreInt32(&pool.BeRwConnCount, int32(iv))
			atomic.StoreInt32(&pool.BeRdConnCount, int32(iv))
		case "be_rw_conns":
			iv, _ := strconv.Atoi(val)
			atomic.StoreInt32(&pool.BeRwConnCount, int32(iv))
		case "be_rd_conns":
			iv, _ := strconv.Atoi(val)
			atomic.StoreInt32(&pool.BeRdConnCount, int32(iv))
		case "be_conn_life_time":
			pool.BeConnLifeTime, _ = strconv.Atoi(val)
		case "msg_buf_size":
			pool.MsgBufSize, _ = strconv.Atoi(val)
		case "be_retry_count":
			iv, _ := strconv.Atoi(val)
			pool.BeRetryCnt = int32(iv)
		case "be_retry_interval":
			iv, _ := strconv.Atoi(val)
			pool.BeRetryInterval = int32(iv)
		case "retry_cnt_when_full":
			iv, _ := strconv.Atoi(val)
			pool.RetryCntWhenFull = int32(iv)
		case "retry_interval_ms_when_full":
			iv, _ := strconv.Atoi(val)
			pool.RetryIntervalWhenFull = int32(iv)
		default:
			return fmt.Errorf("invalid item(%s) in %d line: %s", key, lineno, line)
		}

	}

	for _, pool := range poolMapById {
		poolName := fmt.Sprintf("%s.%s", pool.FeUser, pool.FeDbName)

		tmp_pool := GetPoolByName(poolName)
		if tmp_pool != nil {
			return fmt.Errorf("pool config error, fe_user.fe_dbname (%s) must unique", poolName)
		}
		AddPool(pool)
	}

	return nil
}

func GetNewPool(configFile string, poolID string) (error, *Pool) {

	confFp, err := os.Open(configFile)
	if err != nil {
		return err, nil
	}
	defer func() { confFp.Close() }()

	buff := bufio.NewReader(confFp)

	var key string
	var val string
	var lineno int = 0
	var idx int
	var pool *Pool
	var ok bool
	var poolMapById = make(map[string]*(Pool))

	for {
		line, err := buff.ReadString('\n')
		lineno++

		if err != nil || io.EOF == err {
			break
		}

		line = strings.TrimSpace(line)
		if strings.HasPrefix(line, "#") {
			continue
		}

		if len(line) < 3 {
			continue
		}

		idx = strings.Index(line, "=")
		if len(line) <= 1 {
			continue
		}
		if idx < 0 {
			return fmt.Errorf("invalid line(no '='): %s ", line), nil
		}

		key = line[0:idx]
		key = strings.TrimSpace(key)

		val = line[idx+1:]

		// 把一行后的 以#的注释去掉
		sharpPos := strings.Index(val, "#")
		if sharpPos >= 0 {
			val = val[:sharpPos]
		}
		val = strings.TrimSpace(val)

		if !strings.HasPrefix(key, "pool.") { /*跳过不是以pool.开头的配置*/
			continue
		}

		items := strings.Split(key, ".")
		if len(items) != 3 {
			return fmt.Errorf("invalid item(%s) in %d line: %s", key, lineno, line), nil
		}

		if items[1] != poolID {
			continue
		}

		pool, ok = poolMapById[items[1]]
		if !ok { /*如果没有匹配到则添加到map中*/
			pool = new(Pool)
			pool.Init()
			/*根据配置文件中的 pool.x.y 格式的 key 中的 x 作为 PoolConfig 的 ID 进行赋值*/
			pool.ID = items[1]
			pool.MsgBufSize = 65536
			poolMapById[items[1]] = pool
		}

		switch strings.ToLower(items[2]) {
		case "fe_max_conns":
			iVa, _ := strconv.Atoi(val)
			atomic.StoreInt32(&pool.FeMaxConns, int32(iVa))
		case "fe_user":
			pool.FeUser = val
		case "fe_passwd":
			pool.FePasswd = val
		case "fe_dbname":
			pool.FeDbName = val
		case "be_ipport":
			portalList := strings.Split(val, ",")
			for _, portal := range portalList {
				bePool := new(BePool)
				bePool.Init()
				pool.BePools[portal] = bePool
			}

		case "be_user":
			pool.BeUser = val
		case "be_dbname":
			pool.BeDbName = val
		case "be_passwd":
			pool.BePasswd = val
		case "be_conns":
			iv, _ := strconv.Atoi(val)
			atomic.StoreInt32(&pool.BeRwConnCount, int32(iv))
			atomic.StoreInt32(&pool.BeRdConnCount, int32(iv))
		case "be_rw_conns":
			iv, _ := strconv.Atoi(val)
			atomic.StoreInt32(&pool.BeRwConnCount, int32(iv))
		case "be_rd_conns":
			iv, _ := strconv.Atoi(val)
			atomic.StoreInt32(&pool.BeRdConnCount, int32(iv))
		case "be_conn_life_time":
			pool.BeConnLifeTime, _ = strconv.Atoi(val)
		case "msg_buf_size":
			pool.MsgBufSize, _ = strconv.Atoi(val)
		case "be_retry_count":
			iv, _ := strconv.Atoi(val)
			pool.BeRetryCnt = int32(iv)
		case "be_retry_interval":
			iv, _ := strconv.Atoi(val)
			pool.BeRetryInterval = int32(iv)
		case "retry_cnt_when_full":
			iv, _ := strconv.Atoi(val)
			pool.RetryCntWhenFull = int32(iv)
		case "retry_interval_ms_when_full":
			iv, _ := strconv.Atoi(val)
			pool.RetryIntervalWhenFull = int32(iv)

		default:
			return fmt.Errorf("invalid item(%s) in %d line: %s", key, lineno, line), pool
		}

	}

	for _, pool := range poolMapById {
		poolName := fmt.Sprintf("%s.%s", pool.FeUser, pool.FeDbName)
		tmp_pool := GetPoolByName(poolName)
		if tmp_pool != nil {
			return fmt.Errorf("the pool(poolName=%s) is aready exists", poolName), nil
		}
		return nil, pool
	}

	return fmt.Errorf("get the new pool(id=%s) failed", poolID), nil
}
